/*
 * Copyright (C) 2015 Miquel Sas
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package traderlib.core.data.db;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import traderlib.core.data.entity.Field;
import traderlib.core.data.entity.FieldList;
import traderlib.core.data.entity.Order;
import traderlib.core.data.types.KeyPointer;
import traderlib.core.data.types.OrderKey;

/**
 * A RecordSet packs a list of records.
 *
 * @author Miquel Sas
 */
public class RecordSet {

	/**
	 * The list of records.
	 */
	private List<Record> records = new ArrayList<>();
	/**
	 * The list of columns.
	 */
	private FieldList columns;
	/**
	 * The list of order by key pointers.
	 */
	private List<KeyPointer> orderByKeyPointers;

	/**
	 * Default constructor.
	 */
	public RecordSet() {
		super();
	}

	/**
	 * Returns the list of order by key pointers.
	 *
	 * @return The list of order by key pointers.
	 */
	public List<KeyPointer> getOrderByKeyPointers() {
		if (orderByKeyPointers == null) {
			orderByKeyPointers = getColumnList().getPrimaryKeyPointers();
		}
		return orderByKeyPointers;
	}

	/**
	 * Sets the list of order by key pointers.
	 *
	 * @param orderByKeyPointers The list of order by key pointers.
	 */
	public void setOrderByKeyPointers(List<KeyPointer> orderByKeyPointers) {
		this.orderByKeyPointers = orderByKeyPointers;
	}

	/**
	 * Sets the column list.
	 *
	 * @param columns The column list.
	 */
	public void setColumnList(FieldList columns) {
		this.columns = columns;
	}

	/**
	 * Returns the number of columns.
	 *
	 * @return The number of columns.
	 */
	public int getColumnCount() {
		return columns.size();
	}

	/**
	 * Returns the columns, for use in the friend class Cursor.
	 *
	 * @return The column list.
	 */
	FieldList getColumnList() {
		return columns;
	}

	/**
	 * Get the column at the given index.
	 *
	 * @param index The indx of the column.
	 * @return The column.
	 */
	public Column getColumn(int index) {
		return (Column) columns.getField(index);
	}

	/**
	 * Get a column by alias.
	 *
	 * @param alias The column alias.
	 * @return The column or null if not found.
	 */
	public Column getColumn(String alias) {
		return (Column) columns.getField(alias);
	}

	/**
	 * Clear the list of records.
	 */
	public void clear() {
		records.clear();
	}

	/**
	 * Returns this record set size.
	 *
	 * @return The size.
	 */
	public int size() {
		return records.size();
	}

	/**
	 * Add a record to the list.
	 *
	 * @param record The record to add
	 * @return A boolean indicating ifthe record has been added.
	 */
	public boolean add(Record record) {
		if (record.getColumnList() == null) {
			record.setColumnList(columns);
		}
		if (columns == null) {
			columns = record.getColumnList();
		}
		return records.add(record);
	}

	/**
	 * Inserts a record at a given index.
	 *
	 * @param index The index.
	 * @param record The record to insert.
	 */
	public void add(int index, Record record) {
		if (record.getColumnList() == null) {
			record.setColumnList(columns);
		}
		if (columns == null) {
			columns = record.getColumnList();
		}
		records.add(index, record);
	}

	/**
	 * Gets the insert index using the order key.
	 *
	 * @param record The record.
	 * @return The insert index.
	 */
	public int getInsertIndex(Record record) {
		int index;
		OrderKey key = record.getIndexKey(getOrderByKeyPointers());
		for (index = 0; index < records.size(); index++) {
			Record scanRecord = records.get(index);
			OrderKey scanKey = scanRecord.getIndexKey(getOrderByKeyPointers());
			if (key.compareTo(scanKey) <= 0) {
				break;
			}
		}
		return index;
	}

	/**
	 * Get a record given its index in the record list.
	 *
	 * @return The Record.
	 * @param index The index in the record list.
	 */
	public Record get(int index) {
		if (index < 0 || index >= size()) {
			throw new ArrayIndexOutOfBoundsException();
		}
		return records.get(index);
	}

	/**
	 * Find the index of the given record.
	 *
	 * @param record The record to find its index.
	 * @return The index of the given record.
	 */
	public int indexOf(Record record) {
		return records.indexOf(record);
	}

	/**
	 * @return If the recordset is empty.
	 * @see java.util.ArrayList#isEmpty()
	 */
	public boolean isEmpty() {
		return records.isEmpty();
	}

	/**
	 * Remove a record given its index in the list.
	 *
	 * @return The removed record.
	 * @param index The index in the list of records.
	 */
	public Record remove(int index) {
		if (index < 0 || index >= size()) {
			throw new ArrayIndexOutOfBoundsException();
		}
		Record record = get(index);
		records.remove(index);
		return record;
	}

	/**
	 * Sets a record given its index in the record list.
	 *
	 * @param index The index in the record list.
	 * @param record The record.
	 */
	public void set(int index, Record record) {
		records.set(index, record);
	}

	/**
	 * Sort this list of records based on the order by key pointers, or in its default the primary key pointers.
	 */
	public void sort() {
		if (size() == 0) {
			return;
		}
		sort(getOrderByKeyPointers());
	}

	/**
	 * Sort this list of records based on the order based on a list of key pointers.
	 *
	 * @param keyPointers The list of key pointers
	 */
	public void sort(List<KeyPointer> keyPointers) {
		sort(new RecordComparator(keyPointers));
	}

	/**
	 * Sort this list of records based on the order stated by the argument order.
	 *
	 * @param order The <code>Order</code> to use in the sort.
	 */
	public void sort(Order order) {
		List<Field> fields = new ArrayList<>();
		fields.addAll(getColumnList().getFields());
		sort(order.getKeyPointers(fields));
	}

	/**
	 * Sort this list of records based on a comparator.
	 *
	 * @param comparator
	 */
	public void sort(Comparator<Record> comparator) {
		Record[] recordArray = toArray();
		Arrays.sort(recordArray, comparator);
		records.clear();
		for (Record record : recordArray) {
			records.add((Record) record);
		}
	}

	/**
	 * Returns an array containing all the records.
	 *
	 * @return The array of records.
	 */
	public Record[] toArray() {
		return records.toArray(new Record[records.size()]);
	}
}
